import { getAPIHost } from './address'
import { TOKEN_STORAGE } from '@/common/constants'
import { ErrorCode, SuccessCode } from '@/api/errorCode'
import { isH5 } from '@/env'
import * as api from './api'
import config from '@/config'
import { showToast } from '@/common/utils/toast'
import store from '@/store'

class RequestQueue {
  constructor () {
    this.queue = []
  }

  static getInstance () {
    if (!this.instance) {
      this.instance = new RequestQueue()
    }
    return this.instance
  }

  pushQueue (callback) {
    this.queue.push(callback)
  }

  execQueue (header) {
    this.queue.forEach((item, idx) => {
      item(header)
    })
  }

  clearQueue () {
    this.queue = []
  }
}

class Request {
  constructor (host) {
    this.host = host
  }

  setToken (token) {
    this.token = token
  }

  // 前置流程
  async frontStep () {
    let checkLogining = false
    try {
      checkLogining = await wxCheckLogin()
    } catch (e) {
      console.log('checkError', e)
    }
    try {
      const saveToken = uni.getStorageSync(TOKEN_STORAGE)
      if (!checkLogining || !saveToken) {
        const loginCode = await wxLogin()
        const loginRes = await api.userLogin(loginCode)
        const token = loginRes.token
        uni.setStorageSync(TOKEN_STORAGE, token)
        const tokenResult = await api.getAuthToken(config.activityId)
        this.setToken(tokenResult.token)
      } else {
        const tokenResult = await api.getAuthToken(config.activityId)
        this.setToken(tokenResult.token)
      }
    } catch (e) {}
  }

  async sendRequest (method, path, data, config) {
    if (isH5()) {
      if (store.state.channel) {
        this.channel = store.state.channel
      }
      return this.sendRequestInner(method, path, data, config)
    }
    if (!this.token && (!config || !config.withOutLogin)) {
      if (!this.requestToken) {
        this.requestToken = true
        await this.frontStep()
        this.requestToken = false
        RequestQueue.getInstance().execQueue(true)
        RequestQueue.getInstance().clearQueue()
        return this.sendRequestInner(method, path, data, config)
      } else {
        return new Promise((resolve, reject) => {
          RequestQueue.getInstance().pushQueue(async (isExec) => {
            console.log('isExec', isExec)
            if (isExec) {
              try {
                const result = await this.sendRequestInner(method, path, data, config)
                resolve(result)
              } catch (e) {
                reject(e)
              }
            }
          })
        })
      }
    }
    return this.sendRequestInner(method, path, data, config)
  }

  async sendRequestInner (method, path, data, config) {
    return new Promise((resolve, reject) => {
      const requestArgs = {
        url: this.host + path,
        withCredentials: true,
        data,
        header: config?.header || {},
        method: method,
        success: async (res) => {
          const resData = res.data
          const headerKeyMap = Object.keys(res.header).map(m => m.toLowerCase())
          if (!resData) {
            reject(resData)
            return
          }
          if (resData.code === SuccessCode) {
            // console.log(resData.data);
            resolve(resData.data)
          } else if (resData.code === ErrorCode.TOKEN_ERROR && !isH5()) {
            uni.removeStorageSync(TOKEN_STORAGE)
            this.setToken(null)
            if (!config || !config.isRetry) {
              try {
                const result = await this.sendRequest(method, path, data, {
                  isRetry: true
                })
                resolve(result)
              } catch (e) {
                reject(e)
              }
            } else {
              // eslint-disable-next-line
              reject('retry')
            }
          } else if (headerKeyMap.includes('alb-qps-limited')) {
            showToast('服务繁忙，请稍后重试')
            // eslint-disable-next-line
            return reject({})
          } else {
            reject(resData)
          }
        },
        fail: (error) => {
          error.errorUrl = requestArgs.url
          reject(error)
        },
        timeout: 10000
      }
      if (this.token != null) {
        requestArgs.header.xcxtoken = this.token
      }
      if (this.channel) {
        requestArgs.header['ccc-channel'] = this.channel
      }
      uni.request(requestArgs)
    })
  }
}

export function wxLogin () {
  return new Promise((resolve, reject) => {
    uni.login({
      provider: 'weixin',
      success: function (loginRes) {
        // const seconds = new Date().getSeconds();
        // if (seconds % 20 < 10) {
        //     reject(loginRes.code);
        // } else {
        //     resolve(loginRes.code);
        // }
        // console.log(seconds);
        resolve(loginRes.code)
      },
      fail: function (err) {
        reject(err)
      }
    })
  })
}

function wxCheckLogin () {
  return new Promise((resolve, reject) => {
    uni.checkSession({
      success: function (loginRes) {
        resolve(true)
      },
      fail: function (err) {
        resolve(false)
      }
    })
  })
}

export const APIRequest = new Request(getAPIHost())
